
/*
This file is part of [ahttpserver] project. 

Author: Artem Kustikov (kustikoff[at]tut.by)

This code is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this code.

Permission is granted to anyone to use this code for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:

1. The origin of this code must not be misrepresented; you must
not claim that you wrote the original code. If you use this
code in a product, an acknowledgment in the product documentation
would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original code.

3. This notice may not be removed or altered from any source
distribution.


ode based on following document:

		FastCGI Specification
		Mark R. Brown
		Open Market, Inc.

		Document Version: 1.0
		29 April 1996

Some code logic was copied from Open Market FastCGI C++ library (fcgi-2.4.0).
To avoid licensing issues attached original license file - "LICENSE.TERMS".
*/



#ifndef FASTCGI_UTIL_HPP
#define FASTCGI_UTIL_HPP

#include "aconnect/types.hpp"
#include "aconnect/util.hpp"
#include "aconnect/complex_types.hpp"

#include <boost/noncopyable.hpp>

#include "fastcgi_application_manager.hpp"

namespace fastcgi 
{	
	class Stream : private boost::noncopyable
	{
	
	public:
		Stream (SocketDescriptor sd, 
			size_t bufferSize = 2 * MaxMessageContentLength,
			int selectTimeout = fastcgi::os::DefaultSocketSelectTimeout);

		virtual ~Stream();

		inline size_t getAvailableSize() { return _nextPos - _currentPos; }

	protected:
		SocketDescriptor _sd;
		int _selectTimeout;
		
		size_t _bufferSize;
		byte_type *_buffer;
		byte_type *_nextPos;
		byte_type *_currentPos;
	};

	class InputStream : public Stream
	{
	public:
		InputStream (SocketDescriptor sd, 
			size_t bufferSize = 2 * MaxMessageContentLength,
			int selectTimeout = fastcgi::os::DefaultSocketSelectTimeout);

		virtual ~InputStream();

		size_t readByte (byte_type& byte)  throw (fastcgi_error);

		aconnect::string read (size_t length)  throw (fastcgi_error);

		template<typename T>
		size_t readContentTo (size_t contentLength, byte_type paddingLength,
			void (T::*memFun)(aconnect::string_constptr, size_t), T* obj)  throw (fastcgi_error);

		size_t readBlock (byte_type* buff, size_t length)  throw (fastcgi_error);

		bool readHeader (Header &head)  throw (fastcgi_error);
		void fillBuffer ()  throw (fastcgi_error);
	};


	template<typename T>
	size_t InputStream::readContentTo (size_t contentLength, byte_type paddingLength, 
		void (T::*memFun)(aconnect::string_constptr, size_t), T* obj)  throw (fastcgi_error)
	{
		assert (memFun);
		assert (obj);

		size_t availSize = getAvailableSize();
		size_t fullLength = contentLength + paddingLength;

		if (fullLength <= availSize) 
		{
			(obj->*memFun) ((aconnect::string::value_type*) _currentPos, contentLength);
			_currentPos += fullLength;

			return fullLength;
		}
		
		size_t remainLength = contentLength;

		while (remainLength > 0) {
						
			if (availSize > 0) {
				
				size_t bytesToCopy = aconnect::util::min2 (remainLength, availSize);
				(obj->*memFun) ((aconnect::string::value_type*) _currentPos, bytesToCopy);
				
				_currentPos += bytesToCopy;
				remainLength -= bytesToCopy;
			}

			if (remainLength > 0) {
				fillBuffer();
				availSize = getAvailableSize();
				
				if (availSize == 0) // data is not available
					return (contentLength - remainLength);
			}
		}

		if (paddingLength > 0)
		{
			aconnect::string padding = read(paddingLength);
			if (padding.size() != paddingLength)
				return fullLength - padding.size();
		}
		
		
		return fullLength;
	}
}

#endif // FASTCGI_UTIL_HPP
